<?php
/**
 * 数组操作类
 * User: 小菜鸟儿
 * Email: 496651923@qq.com
 */

namespace library;

class ArrayOperate
{
    /**
     * 结果集数组格式化
     * @param array $result 数据库查询结果集
     * @param string $by_filed 格式化后数组的key,默认为ID
     * @return array 返回格式化后数组
     */
    public static function arrayFormat($result = [],$by_filed = 'id'){
        $format_array = [];
        foreach ($result as $row){
            $format_array[$row[$by_filed]] = $row;
        }
        return $format_array;
    }

    /**
     * 两个多维数组合并（后面值覆盖前面值）
     * @param array $a1 多维数组
     * @param array $a2 多维数组, 用"delete"标记删除的键值
     * @return array 返回合并后数组
     */
    public static function arrayOverlay($a1 = [], $a2 = [])
    {
        $diff = array_diff_key($a2,$a1);
        foreach($diff as $k => $v){
            $a1[$k] = $v;
        }
        foreach($a1 as $k => $v) {
            if (isset($a2[$k]) && $a2[$k]==="delete"){
                unset($a1[$k]);
                continue;
            };
            if(!array_key_exists($k,$a2)) continue;
            if(is_array($v) && is_array($a2[$k])){
                $a1[$k] = self::arrayOverlay($v,$a2[$k]);
            }else{
                $a1[$k] = $a2[$k];
            }
        }
        return $a1;
    }
    static public $treeList = '';
    /**
     * 无限级分类
     * @access public
     * @param Array $data     //数据库里获取的结果集
     * @param Int $pid
     * @param Int $count       //第几级分类
     * @return Array $treeList
     */
    public static function arrayToTreeOption(&$data,$pid = 0,$count = 0,$set=[])
	{
        foreach ($data as $key => $value){
            if($value['parent_id']==$pid){
            	if(isset($set['exclude_id']) && $value['id']==$set['exclude_id']){
        			foreach ($data as $k => $v){
        				if(strpos($v['path'],$value['path'])!==false){
        					unset($data[$k]);
						}
					}
				}else{
					$selected='';
					if(isset($set['current_id']) && $value['id']==$set['current_id']) $selected="selected";

					$option = '<option value="' . $value["id"] . '" ' . $selected . '>';
					for ($i = 0; $i < $count; $i++) {
						$option .= '|- ';
					}
					$option .= '' . $value["title"] . '</option>';
					self::$treeList .= $option;
				}
				unset($data[$key]);
				self::arrayToTreeOption($data, $value['id'], $count + 1, $set);
            }
        }
        return self::$treeList;
    }
    /**
     * 数组转树形(无限级)
     * @param array $data 输入数组，二维结构
     * @param array $set  配置数组
     * @example "[
     *  'field'  => [ //字段设置
     *     'self' => 当前记录KEY，默认'id'
     *     'parent' => 父级标记KEY，默认'pid'
     *   ],
     *   'root' => 起始节点，默认'0'(最上层父级ID)
     * ]"
     * @return array 结果数组
     */
    public static function arrayToTree(&$data,$set = []){
        if(!is_array($data) || !is_array($set) || !count($data)){
            return array();
        }
        /* 初始化设置 */
        $default_set = array(
            'field' => array(
                'self' => 'id',
                'parent' => 'parent_id',
            ),
            'function' => array(
                'format_data' => ''
            ),
            'root' => '0',
            'level' => 0,
        );
        $set = self::arrayOverlay($default_set,$set);
        $field_self = $set['field']['self'];
        $field_parent = $set['field']['parent'];
        $f = $set['function']['format_data'];
        $tree = [];
			
			
        //do{
        foreach($data as $key => $row){
			$row['level'] = $set['level'];
            $temp_row = '';
            if(is_array($set['root'])){
                foreach ($set['root'] as $root_val) {
                    if ($row[$field_parent] == $root_val) {
                        $set['root']=$root_val;
                        if (is_callable($f)) {
                            $temp_row = call_user_func_array($f, [$row, $set]);
                        }
                        $tree[$row[$field_self]]['data'] = $temp_row ? $temp_row : $row;
                        unset($data[$key]);
                    }
                }
            }else {
                if ($row[$field_parent] == $set['root']) {
                    if (is_callable($f)) {
                        $temp_row = call_user_func_array($f, [$row, $set]);
                    }
                    $tree[$row[$field_self]]['data'] = $temp_row ? $temp_row : $row;
                    unset($data[$key]);
                }
            }
        }
        foreach($tree as $key => $value){
            $temp_set = $set;
            $temp_set['root'] = $key;
            $temp_set['level']++;
            $tree[$key]['child'] = self::arrayToTree($data,$temp_set);
			
			
			if(empty($tree[$key]['child'])){
				$tree[$key]['data']['is_have_sub'] = 0;
			}else{
				$tree[$key]['data']['is_have_sub'] = 1;
			}
			
        }
        //}while(($set['level'] == 0) && count($data));
        return $tree;
    }

    /**
     * 树转数组
     * @param  array $tree 树型数组（data为父级数据，child为子级数据）
     * @return array 返回转换后数组
     */
    public static function treeToArray($tree = []){
        $data = [];
        foreach($tree as $row){
            $data[$row['data']['id']] = $row['data'];
            if($row['child']){
                $data = $data + self::treeToArray($row['child']);
            }
        }
        return $data;
    }

    /**
     * 树内搜索
     * @param array $tree 树型数组（data为父级数据，child为子级数据）
     * @param string $key 数据
     * @return array 返回整个节点数据
     */
    public static function searchInTree($tree,$key = ''){
        if(!is_array($tree)) return '';
        if(array_key_exists($key,$tree)) return $tree[$key];
        foreach($tree as $value){
            $row = self::searchInTree($value,$key);
            if($row) return $row;
        }
    }

    /**
     * 树形显示
     * @param		array		$tree		输入数组
     * @param		array		$set		array( //配置数组
     * 		'field'				=>	array( //字段设置
     * 			'self'				=>	当前记录KEY，默认'id'
     * 			'parent'			=>	父级标记KEY，默认'pid'
     * 			'relation'		=>	外键KEY，用于下拉框级联操作，默认''
     * 		),
     * 		'function'		=>	array( //回调函数
     * 			'replace_self'		=>	替换子级的回调函数
     * 			'replace_parent'	=>	替换父级的回调函数
     * 		),
     * 		'node'				=>	起始节点，默认'0'（最上层父级ID）
     * 		'level'				=>	层级，默认'-1'
     * 		'limit'				=>	最大层级，默认'999'
     * 		'prefix'			=>	显示前缀，默认'|- '
     * 		'add_prefix'	=>	递增前缀，默认'&nbsp;&nbsp;&nbsp;'
     * 	)
     * @param		array		$pipe		array( //返回信息数组(待扩充)
     * 		'return_line' =>	最终节点回归线(默认标记第一个最终节点的回归线)
     * 		'parent'			=>	所有父级数据
     * 		'child'				=>	所有子级数据（最后一级子集）
     * )
     * @return	string	$result		结果模版
     */
    public static function treeToShow($tree,$set=array(),&$pipe = array()){
        if(!is_array($tree) || !is_array($set) || !count($tree)){
            return '';
        }

        /* 初始化设置 */
        $default_set = array(
            'show' => 'title',
            'field' => array(
                'self' => 'id',
                'parent' => 'parent_id',
                'relation' => '',
            ),
            'function' => array(
                'replace_self' => '',
                'replace_parent' => '',
            ),
            'node' => '0',
            'level' => '-1',
            'limit' => '999',
            'prefix' => '|— ',
            'add_prefix' => '&nbsp;&nbsp;&nbsp;',
            'return_line' => array()
        );
        $set = self::arrayOverlay($default_set,$set);
        $default_pipe = array(
            'return_line' => array(
                'node' => -1,
                'data' => array(),
            ),
            'parent' => array(),
            'child' => array(),
        );
        $pipe = self::arrayOverlay($default_pipe,$pipe);
        $field_self = $set['field']['self'];
        $field_parent = $set['field']['parent'];
        $result = '';
        /* 循环生成结构 */
        foreach($tree as $key => $row){
            $temp_set = $set;
            $temp_set['level'] += 1;
            if($temp_set['level']>$set['limit']) continue;
            $temp_set['prefix'] = $set['add_prefix'] . $set['prefix'];
            if(!is_array($row['child']) || !count($row['child'])){
                $f = $set['function']['replace_self'];
                $pipe['child'][$row['data'][$field_self]] = $row['data'];
            }else{
                $temp_set['node'] = $key;
                $row['data']['tpl_child'] = self::treeToShow($row['child'],$temp_set,$pipe);
                $f = $set['function']['replace_parent'];
                $pipe['parent'][$row['data'][$field_self]] = $row['data'];
            }
            switch($pipe['return_line']['node']){
                case -1:
                case $key:
                    $pipe['return_line']['node'] = $row['data'][$field_parent];
                    $pipe['return_line']['data'][] = $row['data'];
                    $temp_set['return_line'][] = $key;
                    $set['return_line'][] = $key;
                    break;
                default:
                    ;
            }
            $result .= call_user_func_array($f,array($row['data'],$temp_set));
        }
        return $result;

    }

    /**
     * 数组变成下拉框(无限级)
     * @param	array	$data	原始数组（数据库查出的结果集）
     * @param	int		$id		当前菜单ID
     * @param	array	$set	配置数组(参照treeToShow函数)
     * @return	string	$result	下拉框的option
     **/
    public static function arrayToSelect($data = [],$id = '-2', $set = []){
        $tree = self::arrayToTree($data,$set);
        return self::treeToSelect($tree,$id,$set);
    }
    public static function arrayToTreexc($data = [],$id = '-2', $set = []){
        return self::arrayToTree($data,$set);
    }

    /**
     * 树型变成下拉框(无限级)
     * @param	array	$tree	树型数组（data为父级数据，child为子级数据）
     * @param	int		$id		当前菜单ID
     * @param	array	$set	配置数组(参照treeToShow函数)
     * @return	string	$result	下拉框的option
     **/
    public static function treeToSelect($tree = [],$id = '-2', $set = []){
        $set['now'] = is_null($id) ? [] : [$id];
        $set['function']['replace_self'] = 'self::selectSelf';
        $set['function']['replace_parent'] = 'self::selectParent';
        $result = self::treeToShow($tree,$set);
        return $result;
    }

    /**
     * 数组变成下拉框，子级回调函数
     * @param		array		$row		当前数据
     * @param		array		$set		配置数组(参照treeToShow函数)
     * @return	string	$result	结果模版
     **/
    public static function selectSelf($row,$set=array()){
        $tpl = '<option data-level="%level" data-parent="%parent" value="%id" %selected>%title</option>';
        if(in_array($row['id'],$set['now'])){
            $replace['%selected'] = 'selected="selected"';
        }
        else{
            $replace['%selected'] = '';
        }
        $replace['%id'] = $row['id'];
        $replace['%parent'] = $set['field']['relation']?$row[$set['field']['relation']]:$row[$set['field']['parent']];
        $replace['%level'] = $set['level'];
        $replace['%title'] = $set['prefix'] . $row[$set['show']];
        foreach($replace as $k => $v){
            $replace_a[] = $k;
            $replace_b[] = $v;
        }
        return str_replace($replace_a,$replace_b,$tpl);
    }

    /**
     * 数组变成下拉框，父级回调函数
     * @param array $row 当前数据
     * @param array $set 配置数组(@see treeToShow)
     * @return string 结果模版
     */
    public static function selectParent($row,$set=array()){
        if($set['level'] == 0){
            $tpl = '';
        }
        $tpl = '<option data-level="%level" data-parent="%parent" value="%id" %selected>%title</option>%tpl_child';
        if(in_array($row['id'],$set['now'])){
            $replace['%selected'] = 'selected="selected"';
        }else{
            $replace['%selected'] = '';
        }
        $replace['%id'] = $row['id'];
        $replace['%parent'] = $set['field']['relation']?$row[$set['field']['relation']]:$row[$set['field']['parent']];
        $replace['%level'] = $set['level'];
        $replace['%title'] = $set['prefix'] . $row[$set['show']];
        $replace['%tpl_child'] = $row['tpl_child'];
        foreach($replace as $k => $v){
            $replace_a[] = $k;
            $replace_b[] = $v;
        }
        return str_replace($replace_a,$replace_b,$tpl);
    }
    /**
     * 获取所有子级数据ID
     * @需求：通过ID获取所有子级数据
     * @流程：
     * 1.获取所有数据
     * 2.转换成树型
     * 3.在树内搜索
     * 4.转换子节点为数组
     * 5.取出子节点ID
     * @param  array  $all     数据
     * @param  string  $id     节点ID
     * @param  boolean $self   是否包含自身
     * @return string  $result 结果数组
     * @example 传入0，返回[0,1,2,3]
     */
    public static function getAllChildIds($all=[], $id = 0,$self=true){
        /*转换成树型*/
        $tree = self::arrayToTree($all);
        /*在树内搜索*/
        $sub_tree = self::searchInTree($tree,$id);
        /*转换子节点为数组*/
        $temp_tree = $self ? [$sub_tree] : $sub_tree['child'];
        $child = self::treeToArray($temp_tree);
        /*取出子节点ID*/
        $child_ids = array_keys($child);
        return $child_ids;
    }

    /**
     *	根据字段的值进行数组过滤
     *	@param	$arr	要过滤的数组
     *	@param	$key	要过滤的值
     */
    public static function assocUnique($arr, $key) {
        $tmp_arr = array ();
        foreach ( $arr as $k => $v ) {
            if (in_array ( $v [$key], $tmp_arr )) { // 搜索$v[$key]是否在$tmp_arr数组中存在，若存在返回true
                unset ( $arr [$k] );
            } else {
                $tmp_arr [] = $v [$key];
            }
        }
        return $arr;
    }


    /**
     * 	产生导航树
     *	@param	$list  结果集
     *	@param	$pk ID号
     *	@param	$pid	父id
     *	@param	$child	子树字段
     *	@param	$root
     *	@param	$key
     */
    public static function listToTree($list, $pk = 'id', $pid = 'pid', $child = '_child', $root = 0, $key = '') {
        // 创建Tree
        $tree = array ();
        if (is_array ( $list )) {
            // 创建基于主键的数组引用
            $refer = array ();
            foreach ( $list as $k => $data ) {
                $refer [$data [$pk]] = & $list [$k];
            }
            foreach ( $list as $k => $data ) {
                // 判断是否存在parent
                $parentId = $data [$pid];
                if ($root == $parentId) {
                    if ($key != '') {
                        $tree [$data [$key]] = & $list [$k];
                    } else {
                        $tree [] = & $list [$k];
                    }
                } else {
                    if (isset ( $refer [$parentId] )) {
                        $parent = & $refer [$parentId];
                        if ($key != '') {
                            $parent [$child] [$data [$key]] = & $list [$k];
                        } else {
                            $parent [$child] [] = & $list [$k];
                        }
                    }
                }
            }
        }
        return $tree;
    }

}